# Copyright 2009 Animesh Pathak
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import logging

#for twitter posting
import urllib2, urllib

from google.appengine.api import users

from google.appengine.ext import db

import django
#from django import http
from django import shortcuts
from django.http import HttpResponse
from django.http import HttpResponseRedirect
from django.http import HttpResponseNotFound
from django.shortcuts import render_to_response

from google.appengine.ext.db import djangoforms
from models import RawPicture, Picture, Comment, UserSettings

from google.appengine.api import images

#for sending emails, notification and otherwise
from google.appengine.api import mail


logging.getLogger().setLevel(logging.DEBUG)


#Max width and height allowed by us

MAX_WIDTH = 600
MAX_HEIGHT = 600
THUMBNAIL_MAX_WIDTH = 100
THUMBNAIL_MAX_HEIGHT = 100


class PictureForm(djangoforms.ModelForm):
  class Meta:
    model = Picture
    exclude = ['author', 'date', 'image', 'thumbnail', 'rawImage']

class APIPictureForm(djangoforms.ModelForm):
  class Meta:
    model = Picture
    exclude = ['author', 'date', 'image', 'thumbnail', 'rawImage']

class CommentForm(djangoforms.ModelForm):
  class Meta:
    model = Comment
    exclude = ['author', 'date', 'postedOn']

class UserSettingsForm(djangoforms.ModelForm):
  class Meta:
    model = UserSettings
    exclude = ['owner', 'email']

def index(request):
  """The response shown when a user fetches the main page"""
  body_text = """How about <a href="/add">adding some pictures</a> (or <a href="/listall">seeing all of them</a>)?"""
  # setting up user and login/logout urls
  (user, user_url) = user_setup(request)
  return render_to_response('index.html', locals())

def about(request):
  """About us page"""
  # setting up user and login/logout urls
  (user, user_url) = user_setup(request)
  return render_to_response('about.html', locals())  
  
def showerror(request):
  """Error page"""
  # setting up user and login/logout urls
  (user, user_url) = user_setup(request)
  return render_to_response('error.html', locals())  

def settings(request):
  """GET shows user settings form. POST processes the form"""  
  # setting up user and login/logout urls
  (user, user_url) = user_setup(request)
  if not user:
    #change to a redirect to login page	  
    return HttpResponseRedirect(users.create_login_url(request.path))
  if request.method == 'POST':
    #first check if settings already exist
    uSettingsQ = UserSettings.gql("WHERE owner = :1", user)
    uSettings = uSettingsQ.get()
    userSettingsForm = UserSettingsForm(request.POST, instance=uSettings)

#    userSettingsForm = UserSettingsForm(request.POST)
    if userSettingsForm.is_valid():
      uSettings = userSettingsForm.save(commit=False)
      uSettings.owner = user
      uSettings.email = user.email()
      uSettings.put()
      #TODO Show toast about "user settings saved"
      return HttpResponseRedirect('/settings')
  else:
    #TODO: userSettingsForm = get usersettings with owner = user.
    # if userSettings = None, then call constructor
    uSettingsQ = UserSettings.gql("WHERE owner = :1", user)
    uSettings = uSettingsQ.get()
    if not uSettings:
      #this user has never saved settings
      userSettingsForm = UserSettingsForm()
    else:
      #some settings already exist.
      userSettingsForm = UserSettingsForm(instance=uSettings)
    return render_to_response('settings.html', locals())

	
def add(request):
  """GET shows an add form. POST processes the form"""
  # setting up user and login/logout urls
  (user, user_url) = user_setup(request)

  if not user:
    #change to a redirect to login page	  
    return HttpResponseRedirect(users.create_login_url(request.path))
  
  if request.method == 'POST':
    pictureForm = PictureForm(request.POST)
    if pictureForm.is_valid():
      picture = pictureForm.save(commit=False)
      picture.author = user
      #TODO resize image
      imagedict = request.FILES['image']
      
      # convert image to PNG and save as raw image
      rawImage = RawPicture()
      rawImage.image = imagedict["content"]
      #TODO convert to PNG
      logging.debug("putting rawImage now")
      rawImage.put()


      image = resize_image(imagedict["content"], MAX_WIDTH, MAX_HEIGHT)
      picture.image = db.Blob(image) 
      picture.thumbnail = db.Blob(resize_image(image, THUMBNAIL_MAX_WIDTH, THUMBNAIL_MAX_HEIGHT))
      picture.rawImage = rawImage
      picture.put()
	  

      #posting on twitter now
      uSettingsQ = UserSettings.gql("WHERE owner = :1", user)
      uSettings = uSettingsQ.get()
      if uSettings:
        pic_url = 'http://%s/i/%s' %(request.META['SERVER_NAME'] , picture.key().id())
        image_url = 'http://%s/r/%s' %(request.META['SERVER_NAME'] , picture.key().id())
        #logging.debug("Twitter notification is: %s" % uSettings.notifyTwitter)
        if uSettings.notifyTwitter:
          twitterStatusCode = post_tweet(uSettings.twitterUsername, uSettings.twitterPassword, pic_url, picture.title)
          #twitter_text = "\nAlso posted on Twitter. Status code: %s" % twitterStatusCode
        #posting on blogger
        #blogger_text=""
        if uSettings.notifyBlogger:
          notify_blogger(picture.title, pic_url, image_url, uSettings.bloggerEmail)

      #TODO Test for image being too big by exception
      return HttpResponseRedirect('/i/%s' % picture.key().id())
  else:
    picture_form = PictureForm()
    return render_to_response('upload.html', locals())

def show(request, image_id):
  """Shows the page with the image"""
  # setting up user and login/logout urls
  (user, user_url) = user_setup(request)

  picture = Picture.get_by_id(long(image_id))
  if not picture:
    return HttpResponseNotFound("oops. Picture not found. Please go back and try again.")
  commentForm = CommentForm()
  comments = picture.comment_set

  return render_to_response("showpicture.html", locals())
  
def rawimage(request, image_id):
  """returns the raw image"""
  picture = Picture.get_by_id(long(image_id))
  if not picture:
    return HttpResponseNotFound("Image not Found")
  response = HttpResponse()
  response['Content-Type'] = "image/png"
  response['Cache-Control'] = "max-age=86400" #cache for one day
  logging.debug("serving image with caching headers enabled")
  response.write(picture.image)	
  response.flush()
  return response

def originalimage(request, original_image_id):
  """returns the raw, original image"""
  rawPicture = RawPicture.get_by_id(long(original_image_id))
  if not rawPicture:
    return HttpResponseNotFound("Image not Found")
  response = HttpResponse()
  response['Content-Type'] = "image/png"
  response['Cache-Control'] = "max-age=86400" #cache for one day
  logging.debug("serving image with caching headers enabled")
  response.write(rawPicture.image)	
  response.flush()
  return response
  
def rawthumbnail(request, image_id):
  """returns the raw thumbnail"""
  picture = Picture.get_by_id(long(image_id))
  if not picture:
    return HttpResponseNotFound("Image not Found")
  response = HttpResponse()
  response['Content-Type'] = "image/png"
  response['Cache-Control'] = "max-age=86400" #cache for one day
  logging.debug("serving image with caching headers enabled")
  response.write(picture.thumbnail)	
  response.flush()
  return response

def listall(request):
  """returns a list of all images"""
  # setting up user and login/logout urls
  (user, user_url) = user_setup(request)

  pictures = Picture.all()
  pictures.order('-date')
  return render_to_response("listall.html", locals())

def addcomment(request, image_id):
  """POST only. Adds Comment"""
  # setting up user and login/logout urls
  (user,user_url) = user_setup(request)
  if not user:
    #change to a redirect to login page	  
    return HttpResponseRedirect(users.create_login_url(request.path))
  
  if request.method == 'POST':
    commentForm = CommentForm(request.POST)
    if commentForm.is_valid():
      comment = commentForm.save(commit=False)
      comment.author = users.get_current_user()
      picture = Picture.get_by_id(long(image_id))
      if not picture:
        return HttpResponseNotFound("Image not Found")
      comment.postedOn = picture
      comment.put()
      #TODO find user from picture , get his settings, then email him if needed
      sendEmailToAuthorIfNeeded(picture, request, comment.author.nickname(), comment.content)
      return HttpResponseRedirect('/i/%s' % image_id)
    else:
      return HttpResponseNotFound('some error in form. Please resubmit it')	
  else:
    return HttpResponseNotFound("Why was GET used here?")
  
def apiadd(request):
  """allows addition of images using the API. The parameters passed are
  email: full email address corresponding to username
  apipass: pwd stored in settings, used only for uploading images
  image: The image
  title: Caption
  The process will result in sending of an email to the user, to confirm 
  the upload.
  """
  #if request type not = POST,  return failure
  
  apiPictureForm = APIPictureForm(request.POST) #multipart = post?
  if apiPictureForm.is_valid():
    picture = apiPictureForm.save(commit=False)
  
  email = request.POST["email"]
  title = request.POST["title"]
  #Check if the username and pwd correspond to any existing user in the system. If not, fail.
  uSettingsQ = UserSettings.gql("WHERE email = :1 AND apiAuthString = :2", email, request.POST["apipass"] )
  uSettings = uSettingsQ.get()
  if not uSettings:
    return HttpResponseNotFound('settings with that user not found. Have you set up the mobile upload password in your settings?')	

  if not uSettings.enableAPI:
    return HttpResponseNotFound('API upload is disabled in your settings. Please check again.')	

  picture.author = uSettings.owner
  imagedict = request.FILES['image']
  
  # convert image to PNG and save as raw image
  rawImage = RawPicture()
  rawImage.image = imagedict["content"]
  #TODO convert to PNG
  logging.debug("putting rawImage now")
  rawImage.put()


  image = resize_image(imagedict["content"], MAX_WIDTH, MAX_HEIGHT)
  picture.image = db.Blob(image) 
  picture.thumbnail = db.Blob(resize_image(image, THUMBNAIL_MAX_WIDTH, THUMBNAIL_MAX_HEIGHT))
  picture.rawImage = rawImage
  picture.put()

  pic_url = 'http://%s/i/%s' %(request.META['SERVER_NAME'] , picture.key().id())
  image_url = 'http://%s/r/%s' %(request.META['SERVER_NAME'] , picture.key().id())
  settings_url = 'http://%s/settings' % request.META['SERVER_NAME'] 
  
  twitter_text = ""
  #posting on twitter etc.
  logging.debug("Twitter notification is: %s" % uSettings.notifyTwitter)
  #print("Twitter notification is: %s" % uSettings.notifyTwitter)
  if uSettings.notifyTwitter:
    twitterStatusCode = post_tweet(uSettings.twitterUsername, uSettings.twitterPassword, pic_url, title)
    twitter_text = "\nAlso posted on Twitter. Status code: %s" % twitterStatusCode
  
  #posting on blogger
  blogger_text=""
  if uSettings.notifyBlogger:
    notify_blogger(title, pic_url, image_url, uSettings.bloggerEmail)
    blogger_text="\nEmail sent to blogger quickpost address"
  
  message = mail.EmailMessage(sender="AP-PIC Support <animesh@gmail.com>", subject="New Image Uploaded using API")
  message.to = email
  message.body = """
Dear %s:

A new image was just uploaded on your ap-pic account. 
Title: %s
Image Link: %s
Uploader IP: %s%s%s
If this was not you, we suggest visiting your settings at %s and changing your API Password.

Bests,
The AP-Pic bot
""" % (uSettings.owner.nickname(), title, pic_url, request.META['REMOTE_ADDR'], twitter_text,blogger_text,settings_url)

  message.send()
  #print pic_url
  #print settings_url  
  
  #TODO Test for image being too big by exception
  return HttpResponseRedirect('/i/%s' % picture.key().id())
  
  
def resize_image(image_string, max_width, max_height):
  """returns a blob friendly string by resizing an image so that 
  its width is not more than max_width and 
  its height is not more than max_height, while keeping the aspect ratio."""
	
  return images.resize(image_string, width=max_width, height=max_height)

def user_setup(request):
  """Returns tuple with user and user_url"""
  u = users.get_current_user()
  if not u:
    u_url = users.create_login_url(request.path)
  else:
    # user is definitely valid. Create signout  link
    u_url = users.create_logout_url(request.path)
  return (u, u_url)
 
def post_tweet(twitterUsername, twitterPassword, imageURL, imageTitle):
  """Posts a tweet on twitter with the title of the image and link to the pic.
  returns the HTTP code received. 200 is good, 401 is bad."""
  #technique taken from http://www.voidspace.org.uk/python/articles/urllib2.shtml#id5
  # create a password manager
  password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
  
  # Add the username and password.
  # If we knew the realm, we could use it instead of ``None``.
  twitter_url = "http://twitter.com/statuses/update.xml"
  password_mgr.add_password(None, twitter_url, twitterUsername, twitterPassword)
  
  handler = urllib2.HTTPBasicAuthHandler(password_mgr)
  
  # create "opener" (OpenerDirector instance)
  opener = urllib2.build_opener(handler)
  
  #twitter POST data
  statusString = "[pic] %s %s" % (imageTitle, imageURL)
  twitterData =  urllib.urlencode({ "status": statusString})
  
  # use the opener to fetch a URL
  try:
    logging.debug("trying to open connection to %s using uname: %s and pwd: %s" % (twitter_url, twitterUsername, twitterPassword))
    r = opener.open(twitter_url, data=twitterData)
    #print r.info()
    #return success code
    return r.info()['status'].split(" ")[0]
  except IOError, e:
    if hasattr(e, 'reason'):
      logging.debug("twitter query had socket error")
      errorText = 'We failed to reach a server.\nReason: %s'% e.reason
      errorCode = "0"
    elif hasattr(e, 'code'):
      logging.debug("twitter query had server error %s" % e.code)
      errorTextExpanded = 'The server couldn\'t fulfill the request.\nError code: %s'% e.code
      errorCode = e.code
    return errorCode
  
def notify_blogger(title, pic_url, image_url, bloggerEmail):
  """sends an email to the blogger quickpost email ID with the post"""
  bloggerMail = mail.EmailMessage(sender="AP-PIC Support <animesh@gmail.com>", subject=("[mobilePic] %s" % title))
  bloggerMail.html = """<div><a href="%s" target="_blank"><img src="%s"/ border="0"></a></div><div>enabled by <a href="%s" target="_blank">AP-PIC</a></div>""" % (pic_url, image_url, pic_url )
  bloggerMail.to = bloggerEmail
  bloggerMail.send()

def sendEmailToAuthorIfNeeded(picture, request, comment_author, comment_text):
  """Sends email to author about a new comment on his picture"""
  author = picture.author
  pic_url = 'http://%s/i/%s' %(request.META['SERVER_NAME'] , picture.key().id())
  uSettingsQ = UserSettings.gql("WHERE owner = :1", picture.author)
  uSettings = uSettingsQ.get()


  if not uSettings:
    return;

  if uSettings.notifyOnComments:
    commentMail = mail.EmailMessage(sender="AP-PIC Support <animesh@gmail.com>", subject=("[AP-Pic] New Comment Posted on your Picture"))
    commentMail.to = uSettings.email
    commentMail.body = """
Dear %s:

A new comment was just made on your image on ap-pic
Title: %s
Image Link: %s
Commented by: %s
Comment text: %s

Bests,
The AP-Pic bot""" % (uSettings.owner.nickname(), picture.title, pic_url, comment_author, comment_text)
    logging.debug("Sending email about comment at %s to %s " %(pic_url, commentMail.to))
    commentMail.send()


